package cn.ycq.im.chat;

import cn.ycq.im.chat.clients.ChannelAttributeUtils;
import cn.ycq.im.chat.clients.ClientManager;
import com.alibaba.fastjson.JSON;
import io.netty.channel.Channel;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.SimpleChannelInboundHandler;
import io.netty.handler.codec.http.websocketx.TextWebSocketFrame;
import io.netty.handler.codec.http.websocketx.WebSocketFrame;
import io.netty.handler.timeout.IdleStateEvent;
import io.netty.util.IllegalReferenceCountException;
import lombok.extern.slf4j.Slf4j;

import java.util.HashMap;
import java.util.Map;
import java.util.ServiceLoader;

@Slf4j
public class MyWebSocketHandler extends SimpleChannelInboundHandler<WebSocketFrame> {

    private static final Map<CmdType, CmdHandler> CMD_HANDLER_MAP = new HashMap<>();

    static {
        ServiceLoader<CmdHandler> handlers = ServiceLoader.load(CmdHandler.class);

        for (CmdHandler cmdHandler : handlers) {
            CmdType cmdType = cmdHandler.getCmdType();
            CMD_HANDLER_MAP.put(cmdType, cmdHandler);
        }

    }


    @Override
    public void channelUnregistered(ChannelHandlerContext ctx) throws Exception {
        String userName = (String) ChannelAttributeUtils.getAttribute("userName", ctx.channel());
        ClientManager.removeClient(ctx.channel(), userName);
    }

    @Override
    public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
        if(cause instanceof IllegalReferenceCountException){
            channelUnregistered(ctx);
        }
    }

    @Override
    protected void channelRead0(ChannelHandlerContext ctx, WebSocketFrame msg) throws Exception {
        if (msg instanceof TextWebSocketFrame) {
            String text = ((TextWebSocketFrame) msg).text();
            CmdTransport transport = JSON.parseObject(text, CmdTransport.class);
            handleCmd(transport, ctx);
        }
    }

    private void handleCmd(CmdTransport transport, ChannelHandlerContext ctx) {
        CmdType cmd = transport.getCmd();
        log.info(JSON.toJSONString(transport));

        CmdHandler cmdHandler = CMD_HANDLER_MAP.get(cmd);
        cmdHandler.handle(ctx, transport);
    }


    @Override
    public void userEventTriggered(ChannelHandlerContext ctx, Object evt) throws Exception {
        super.userEventTriggered(ctx, evt);
        if(evt instanceof IdleStateEvent){
            switch (((IdleStateEvent) evt).state()){
                case READER_IDLE:
                    channelUnregistered(ctx);
                    ctx.channel().close();
                    break;
            }
        }
    }
}
